package com.zkh.myutils.io.excel.parser;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.zkh.myutils.io.excel.ExcelInputStream;
import com.zkh.myutils.io.excel.ExcelUtils;
import com.zkh.myutils.io.excel.XMLParseException;
import com.zkh.myutils.io.excel.XMLParseUtils;
import com.zkh.myutils.io.excel.data.CellData;
import com.zkh.myutils.io.excel.data.RowData;
import com.zkh.myutils.io.excel.data.SheetData;
import com.zkh.myutils.io.excel.data.WorkbookData;
import com.zkh.myutils.utils.Assert;
import com.zkh.myutils.utils.Regexp;
import com.zkh.myutils.utils.StringUtils;

/**
 * XLSX解析器
 * @author zkh
 *
 */
public class XLSXParser implements ExcelParser{
	//文件流
	private InputStream inputStream;
	//数据对象
	private WorkbookData workbookData = new WorkbookData();
	//sheet名称列表
	private List<String> sheetNameList = new ArrayList<>();
	//共享字符串
	private List<String> sharedStrings = new ArrayList<>();
	//待转换共享字符串
	private List<CellData> sharingList = new ArrayList<>();
	//文件限制大小
	//合并单元格格式正则
	Pattern mergeRegExp = Pattern.compile("([A-Z]+)(\\d+):([A-Z]+)(\\d+)");
	
	/**
	 * 构造方法
	 * @param inputStream 文件流
	 */
	public XLSXParser(InputStream inputStream) {
		this.inputStream = inputStream;
	}
	
	/**
	 * 执行解析
	 */
	public WorkbookData parse() throws IOException {
		//创建Zip流对象
		ExcelInputStream excelInputStream = new ExcelInputStream(inputStream);
		//Entry
		ZipEntry entry;
		//开始遍历
		while((entry=excelInputStream.getNextEntry())!=null) {
			//当前文件
			String name = entry.getName();
			//过滤
			if("xl/sharedStrings.xml".equals(name)) {
				parseSharedStrings(excelInputStream);
			}else if("xl/workbook.xml".equals(name)) {
				parseWorkbook(excelInputStream);
			}else if(name.startsWith("xl/worksheets") && !name.startsWith("xl/worksheets/_rels/")) {
				parseSheet(entry.getName(), excelInputStream);
			}
		}
		//关闭流
		excelInputStream.closeManual();
		//处理共享数据
		sharingList.forEach(cell->cell.setStringValue(sharedStrings.get(cell.getIndex())));
		//处理sheet名称
		workbookData.resetNameMap(sheetNameList);
		//返回数据
		return workbookData;
	}
	
	/**
	 * 解析Workbook
	 */
	private void parseWorkbook(InputStream inputStream) {
		//解析文档
		try {
			DocumentBuilder builder = XMLParseUtils.createDocumentBuilder();
			//开始解析
			Document doc = builder.parse(inputStream);
			//获取sheets节点
			Node node = doc.getElementsByTagName("sheets").item(0);
			//获取子节点
			NodeList nodeList = node.getChildNodes();
			//遍历
			for(int i=0,len=nodeList.getLength(); i<len; i++) {
				//当前sheet
				Node cNode = nodeList.item(i);
				//保存名称
				sheetNameList.add(cNode.getAttributes().getNamedItem("name").getNodeValue());
			}
		}catch (XMLParseException e) {
			throw e;
		}catch(Exception e) {
			throw new XMLParseException(e);
		}
	}
	
	/**
	 * 解析共享字符串
	 */
	private void parseSharedStrings(InputStream inputStream) {
		//解析文档
		try {
			DocumentBuilder builder = XMLParseUtils.createDocumentBuilder();
			//开始解析
			Document doc = builder.parse(inputStream);
			//获取sst节点
			Node node = doc.getElementsByTagName("sst").item(0);
			//获取子节点
			NodeList nodeList = node.getChildNodes();
			//遍历
			for(int i=0,len=nodeList.getLength(); i<len; i++) {
				//当前sheet
				Node cNode = nodeList.item(i);
				//子节点
				Node child = cNode.getFirstChild();
				String cname = child.getNodeName();
				//如果是富文本，则是r
				if("r".equals(cname)) {
					//文本
					StringBuilder sb = new StringBuilder(child.getLastChild().getTextContent());
					//下一个节点还是r
					while(((child = child.getNextSibling())!=null) && "r".equals(child.getNodeName())) {
						sb.append(child.getLastChild().getTextContent());
					}
					//保存
					sharedStrings.add(sb.toString());
				}//普通字符串为t
				else if("t".equals(cname)){
					//保存值
					sharedStrings.add(cNode.getFirstChild().getTextContent());
				}
			}
		}catch (XMLParseException e) {
			throw e;
		}catch(Exception e) {
			throw new XMLParseException(e);
		}
	}
	
	/**
	 * 解析sheet
	 * @param sheetName sheet名称
	 */
	private void parseSheet(String sheetName, InputStream inputStream) {
		//解析文档
		try {
			DocumentBuilder builder = XMLParseUtils.createDocumentBuilder();
			//开始解析
			Document doc = builder.parse(inputStream);
			//数据对象
			SheetData sheetData = new SheetData();
			//获取数据节点
			NodeList rowList = doc.getElementsByTagName("sheetData").item(0).getChildNodes();
			//最大列
			int maxCol = 0;
			//前一行
			int preRow = 1;
			//遍历
			for(int i=0,len=rowList.getLength(); i<len; i++) {
				//行
				Node row = rowList.item(i);
				//当前行
				int currRow = Integer.parseInt(row.getAttributes().getNamedItem("r").getNodeValue());
				//行数据容器
				List<CellData> rowData = new ArrayList<>();
				//行节点并获取列节点
				NodeList colList = row.getChildNodes();
				//行内最大列
				int rowMaxCol = 0;
				//前一个列
				int preCol = 0;
				//遍历
				for(int j=0, length=colList.getLength(); j<length; j++) {
					//当前单元格节点
					Node cell = colList.item(j);
					//属性
					NamedNodeMap attrs = cell.getAttributes();
					//单元格编号
					String cellId = attrs.getNamedItem("r").getNodeValue();
					//当前列
					int currCol = ExcelUtils.getColumnNumber(Regexp.getMatchOne("[A-Z]+", cellId));
					//更新行内最大列
					if(j==length-1 && currCol>rowMaxCol) {
						rowMaxCol = currCol;
					}
					//单元格类型（b：boolean值，1或0，d：日期，e：error，inlineStr：字符串，n：数字，s：共享字符，str：公式字符）
					String cellType = Assert.getIfNotNull(attrs.getNamedItem("t"), Node::getNodeValue, "n");
					//子节点
					NodeList v = cell.getChildNodes();
					//子节点长度
					int w = v.getLength();
					//单元格值
					CellData data = new CellData();
					//为空
					if(w==0) {
						data = CellData.EMPTY_CELLDATA;
					}else {
						//遍历
						for(int k=0; k<w; k++) {
							//当前节点
							Node t = v.item(k);
							//节点名称
							String name = t.getNodeName();
							//值
							if("v".equals(name)) {
								//值
								String val = t.getTextContent();
								//数字格式（数字、布尔值、日期）
								if(StringUtils.isExist(cellType, "n", "b", "d")) {
									data.setNumberValue(new BigDecimal(val));
								}//共享字符
								else if("s".equals(cellType)) {
									data.setIndex(Integer.parseInt(val));
									//待处理共享字符串
									sharingList.add(data);
								}//其它字符（错误e，公式字符str）
								else {
									data.setStringValue(val);
								}
							}//公式
							else if("f".equals(name)) {
								data.setFormula(t.getTextContent());
							}//is标签，存放inlineStr类型的数据
							else if("is".equals(name)) {
								//在is标签下的t标签
								data.setStringValue(t.getFirstChild().getTextContent());
							}
						}
					}
					//保存空列
					while(preCol++<currCol-1) {
						//添加空列
						rowData.add(CellData.EMPTY_CELLDATA);
					}
					//保存当前列
					rowData.add(data.isEmpty() ? CellData.EMPTY_CELLDATA : data);
				}
				//更新最大列
				if(rowMaxCol>maxCol) {
					maxCol = rowMaxCol;
				}
				//保存空行
				while(preRow++<currRow) {
					sheetData.putEmptyRow();
				}
				//保存当前行
				sheetData.putRowData(rowData);
			}
			//保存列（最大列）
			sheetData.setCols(maxCol);
			//解析合并单元格数据
			parseMergeData(sheetData, doc.getElementsByTagName("mergeCells").item(0));
			//保存Sheet
			workbookData.putData(sheetName, sheetData);
		}catch (XMLParseException e) {
			throw e;
		}catch(Exception e) {
			throw new XMLParseException(e);
		}
	}
	
	/**
	 * 解析合并单元格数据
	 * @param sheetData sheet数据对象
	 * @param mergeCells 合并单元格节点
	 */
	private void parseMergeData(SheetData sheetData, Node mergeCells) {
		//不为空
		if(mergeCells!=null) {
			//获取子节点
			NodeList mergeCellsList = mergeCells.getChildNodes();
			//遍历合并单元格
			for(int i=0,len=mergeCellsList.getLength(); i<len; i++) {
				//当前合并项
				String merge = mergeCellsList.item(i).getAttributes().getNamedItem("ref").getNodeValue();
				//匹配
				String[] parts = getMergePart(merge);
				//合并行
				if(parts[0].equals(parts[2])) {
					//列索引
					int col = ExcelUtils.getColumnNumber(parts[0]) - 1;
					//填充值
					for(int start=Integer.parseInt(parts[1]) + 1, end=Integer.parseInt(parts[3]); start<=end; start++) {
						//复制前一单元格
						CellData preCellData = CellData.clone(sheetData.getRowData(start - 2).getCellData(col));
						//当前单元格值填充为前一单元格值
						sheetData.getRowData(start - 1).setCellData(preCellData, col);
						//新单元格需要解析共享字符串
						sharingList.add(preCellData);
					}
				}//合并列
				else {
					//行索引
					int row = Integer.parseInt(parts[1]);
					//当前单元格值填充为前一单元格值
					RowData rowData = sheetData.getRowData(row-1);
					//填充值
					for(int start = ExcelUtils.getColumnNumber(parts[0]) + 1, end=ExcelUtils.getColumnNumber(parts[2]); start<=end; start++) {
						//新单元格需要解析共享字符串
						sharingList.add(rowData.setPreData(start-1));
					}
				}
			}
		}
	}

	/**
	 * 匹配合并单元格各部分
	 * @param mergeString 合并单元格格式
	 */
	private String[] getMergePart(String mergeString) {
		//匹配
		Matcher matcher = mergeRegExp.matcher(mergeString);
		//匹配到
		if(matcher.matches()) {
			return new String[] {matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4)};
		}
		//这一步到不了
		return null;
	}
}
